using System;
using System.ComponentModel;
using System.Windows.Forms;

namespace RapidHDL
{

	public class EventNode
	{
		private EventNode forwardEventNode;
		private EventNode backEventNode;
		private long eventTime;
		private Clock.ClockEvent clockEvent;

		public EventNode ForwardEventNode
		{
			get{return forwardEventNode;}
			set{forwardEventNode = value;}
		}

		public EventNode BackEventNode
		{
			get{return backEventNode;}
			set{backEventNode = value;}
		}
			
		public long EventTime
		{
			get{return eventTime;}
		}

		public void DoClockEvent(long pTimeNow)
		{
			clockEvent(pTimeNow);
		}

		public EventNode(long pEventTime, Clock.ClockEvent pClockEvent)
		{
			eventTime = pEventTime;
			clockEvent = pClockEvent;
		}

	}

	class EventList
	{
		EventNode firstNode;
		EventNode lastNode;

		public void UnlinkNode(EventNode pEventNode)
		{ 
			try
			{
				if (pEventNode == firstNode)
					firstNode = pEventNode.BackEventNode;
				if(pEventNode == lastNode)
					lastNode = pEventNode.ForwardEventNode;

				if (pEventNode.ForwardEventNode != null)
					pEventNode.ForwardEventNode.BackEventNode = pEventNode.BackEventNode;

				if(pEventNode.BackEventNode != null)
					pEventNode.BackEventNode.ForwardEventNode = pEventNode.ForwardEventNode;

				pEventNode.ForwardEventNode = null;
				pEventNode.BackEventNode = null;
			}
			catch
			{
				//errors will be errors!
			}
		}

		public EventNode InsertClockEvent(long pEventTime, Clock.ClockEvent pClockEvent)
		{
			EventNode currentEventNode = new EventNode(pEventTime,pClockEvent);
			
			if (firstNode == null)
			{
				firstNode = currentEventNode;
				lastNode = currentEventNode;
				return currentEventNode;
			}

			if (lastNode.EventTime <= pEventTime)
			{
				lastNode.BackEventNode = currentEventNode;
				currentEventNode.ForwardEventNode = lastNode;
				lastNode = currentEventNode;
				return currentEventNode;
			}

			if (firstNode.EventTime > pEventTime)
			{
				firstNode.ForwardEventNode = currentEventNode;
				currentEventNode.BackEventNode = firstNode;
				firstNode = currentEventNode;
				return currentEventNode;
			}

			if (firstNode.EventTime == pEventTime)
			{
				currentEventNode.ForwardEventNode = firstNode;
				currentEventNode.BackEventNode = firstNode.BackEventNode;
				if(!(firstNode.BackEventNode == null))
					currentEventNode.BackEventNode.ForwardEventNode = currentEventNode;
				firstNode.BackEventNode = currentEventNode;
				return currentEventNode;
			}


			EventNode searchNode = lastNode;
			while(searchNode.EventTime > pEventTime)
			{
				searchNode = searchNode.ForwardEventNode;
			}

			currentEventNode.ForwardEventNode = searchNode;
			currentEventNode.BackEventNode = searchNode.BackEventNode;
			if(!(firstNode.BackEventNode == null))
				currentEventNode.BackEventNode.ForwardEventNode = currentEventNode;
			currentEventNode.BackEventNode.ForwardEventNode = currentEventNode;
			searchNode.BackEventNode = currentEventNode;
			return currentEventNode;
		}

		public void DoClockEvents(long pTimeNow)
		{
			if (firstNode == null)
				return;

			if(firstNode.EventTime < pTimeNow)
				throw(new Exception("You can't turn back time."));

			while(firstNode.EventTime == pTimeNow)
			{
				firstNode.DoClockEvent(pTimeNow);
				firstNode = firstNode.BackEventNode;
				if (firstNode == null)
					return;
				firstNode.ForwardEventNode = null;
			}
		}

		public void Reset()
		{
			EventNode node;
			while(firstNode != null)
			{
				node = firstNode;
				firstNode = firstNode.BackEventNode;

				node.BackEventNode = null;
				node.ForwardEventNode = null;
			}

			firstNode = null;
			lastNode = null;
		}
	}

	public class Clock
	{
		public delegate void ClockEvent(long timeNow);
	
		EventList eventList;
		long timeNow;
		double timeStep;

		public RapidHardware RapidHardware;

		public Clock(RapidHardware poRapidHardware)
		{
			RapidHardware = poRapidHardware;
			Frequency = 3000;
			timeNow = 0;
			eventList = new EventList();
		}

		public void Reset()
		{
			timeNow = 0;
			eventList.Reset();
		}

		public long TimeNow
		{
			get{return timeNow;}
		}

		internal int Frequency
		{
			get{return System.Convert.ToInt32(1/timeStep);}
			set{timeStep = 1/ System.Convert.ToDouble(value);}
		}

		public EventNode ScheduleClockEvent(long pClockCycles, ClockEvent pClockEvent)
		{
			double timeDeltaSeconds = pClockCycles * timeStep;
			return ScheduleClockEvent(timeDeltaSeconds,pClockEvent);
		}
		
		public EventNode ScheduleClockEvent(double pTimeDelta, ClockEvent pClockEvent)
		{
			long eventTime = timeNow + (int)(pTimeDelta / timeStep + .5);
			if (pTimeDelta <= 0)
				return null;
			
			if (pTimeDelta > 0 && eventTime == timeNow)
				eventTime = timeNow + 1;

			return eventList.InsertClockEvent(eventTime,pClockEvent);
		}

		public void UnlinkNode(EventNode pEventNode)
		{
			eventList.UnlinkNode(pEventNode);
		}

		public void DoClock(double pSeconds)
		{
			int steps = System.Convert.ToInt32(pSeconds / timeStep + .5);
			DoClock(steps);
		}

		public void DoClock(int pSteps)
		{
			for(int step = 0; step < pSteps; step++)
			{
				timeNow++;
				eventList.DoClockEvents(timeNow);
				RapidHardware.ClckTick(timeNow);
			}
		}

		public double SecondsNow
		{
			get 
			{
				return ((double)timeNow / (double)Frequency);
			}
		}
	}
}

